#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <mutex>

#include "Task.hpp"

template<class T>
class BlockQueue
{
public:
    BlockQueue(size_t capacity = defaultCapacity)
    :_capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_pCond, nullptr);
        pthread_cond_init(&_cCond, nullptr);
    }
    //放内容到队列中
    void Push(const T& in)
    {
        //生产者放入内容
        pthread_mutex_lock(&_mtx);
        //如果队列满了，等待消费者取走内容
        while(_capacity == _tasks.size())
        {
            pthread_cond_wait(&_pCond, &_mtx);
        }
        //放入内容，并唤醒消费者来消费
        _tasks.push(in);
        pthread_cond_signal(&_cCond);
        pthread_mutex_unlock(&_mtx);
    }
    //从队列取内容
    void Pop(T* out)
    {
        //消费者取走内容
        pthread_mutex_lock(&_mtx);
        //如果队列没有内容，等待生产者放入
        while(0 == _tasks.size())
        {
            pthread_cond_wait(&_cCond, &_mtx);
        }
        //取出内容，并唤醒生产者生产
        *out = _tasks.front();
        _tasks.pop();
        pthread_cond_signal(&_pCond);
        pthread_mutex_unlock(&_mtx);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cCond);
        pthread_cond_destroy(&_pCond);
    }
private:
    std::queue<T> _tasks;      //队列
    size_t _capacity;          //容量
    pthread_mutex_t _mtx;      //任务队列锁
    pthread_cond_t _pCond;     //生产者条件变量
    pthread_cond_t _cCond;     //消费者条件变量
};
